package com.iceberg.adgt.ui.architecture;

import com.iceberg.adgt.common.*;
import com.iceberg.adgt.common.Observer;
import com.iceberg.adgt.domain.*;

import java.util.*;

/**
 * @author David
 */
public final class ArchitectureControllerImpl implements ArchitectureController {

	private final ObserverInterestMap oim = new ObserverInterestMap();
	private final DomainFacade facade = DomainFacadeImpl.getInstance();
	private ArchitectureDTO architecture = facade.getArchitecture();
	private String selectedLayer;
	private UnitDTO selectedUnit;

	public ArchitectureControllerImpl() {
		addToObserverList();
	}

	void addToObserverList() {
		facade.addObserver(this, ObserverInterest.ARCHITECTURE_CHANGED);
		facade.addObserver(this, ObserverInterest.LAYERS_CHANGED);
		facade.addObserver(this, ObserverInterest.UNITS_CHANGED);
	}

	@Override
	public void addLayer(String name, int number) throws DomainException {
		facade.addLayer(name, number);
	}

	@Override
	public void removeLayer(String name) {
		if (selectedLayer.equals(name)) {
			selectedLayer = null;
			notifyChange(ObserverInterest.LAYER_SELECTION_CHANGED);
		}
		facade.removeLayer(name);
	}

	@Override
	public Map<String, LayerDTO> getLayers() {
		return architecture.getLayers();
	}

	@Override
	public Set<UnitDTO> getUnitsFromLayer(String name) {
		return architecture.getLayers().get(name).getUnits();
	}

	@Override
	public void addUnitToLayer(String unitName, String type) throws DomainException {
		facade.addUnitToLayer(unitName, type, selectedLayer);
	}

	@Override
	public void removeUnitFromLayer() throws ControllerException {
		if (selectedUnit != null) {
			facade.removeUnitFromLayer(selectedUnit.getName(), selectedUnit.getType().getName(), selectedLayer);
			setSelectedUnit(null);
		} else {
			throw new ControllerException("No unit selected!");
		}
	}

	@Override
	public void setSelectedLayer(String name) {
		selectedLayer = name;
		oim.notifyObservers(ObserverInterest.LAYER_SELECTION_CHANGED);
	}

	@Override
	public String getSelectedLayer() {
		return selectedLayer;
	}

	@Override
	public void setSelectedUnit(UnitDTO unit) {
		selectedUnit = unit;
	}

	@Override
	public void addObserver(Observer observer, ObserverInterest interest) {
		oim.put(interest, observer);
	}

	@Override
	public Map<String, LayerDTO> getAvailableAllowedLayers() throws ControllerException {
		Map<String, LayerDTO> allowedLayers = new HashMap<String, LayerDTO>();

		if (this.selectedLayer != null) {
			Map<String, LayerDTO> allowedLayersForLayer = architecture.getLayers().get(selectedLayer).getAllowedLayers();
			for (LayerDTO layer : architecture.getLayers().values()) {
				//Filter out the selected layer
				if (!layer.getName().equals(selectedLayer)) {

					boolean allreadyContains = false;
					for (LayerDTO containedLayer : allowedLayersForLayer.values()) {
						if (layer.getName().equals(containedLayer.getName())) {
							allreadyContains = true;
						}
					}

					if (!allreadyContains) {
						allowedLayers.put(layer.getName(), layer);
					}
				}
			}
		} else {
			throw new ControllerException("No layer selected");
		}

		return allowedLayers;
	}

	@Override
	public List<Integer> getUsedLayerNumbers() {
		ArrayList<Integer> numbers = new ArrayList<Integer>();

		for (LayerDTO layerDTO : architecture.getLayers().values()) {
			numbers.add(layerDTO.getNumber());
		}

		return numbers;
	}

	@Override
	public Map<String, LayerDTO> getAllowedLayersForLayer() throws ControllerException {

		if (this.selectedLayer != null) {
			return architecture.getLayers().get(selectedLayer).getAllowedLayers();
		} else {
			throw new ControllerException("No layer selected.");
		}
	}

	@Override
	public void addAllowedLayerToLayer(String toLayer) throws DomainException, ControllerException {
		if (selectedLayer != null) {
			facade.addAllowedLayerToLayer(selectedLayer, toLayer);
		} else {
			throw new ControllerException("No layer selected.");
		}
	}

	@Override
	public void removeAllowedLayer(String deleteLayer) throws DomainException, ControllerException {
		if (selectedLayer != null) {
			facade.removeAllowedLayerForLayer(deleteLayer, selectedLayer);
		} else {
			throw new ControllerException("No layer selected.");
		}
	}

	@Override
	public void notifyChange(ObserverInterest interest) {
		if (interest.equals(ObserverInterest.ARCHITECTURE_CHANGED) ||
				interest.equals(ObserverInterest.LAYERS_CHANGED) ||
				interest.equals(ObserverInterest.UNITS_CHANGED)
				) {
			architecture = facade.getArchitecture();
		}
	}
}
